#include "lib/common.h"

#define INIT_SIZE 128

int main(int argc, char **argv) 
{
    int listen_fd, connected_fd;
    int ready_number;
    ssize_t n;
    char buf[MAXLINE];
    struct sockaddr_in client_addr;

    listen_fd = tcp_server_listen(SERV_PORT);

    //初始化pollfd数组，这个数组的第一个元素是listen_fd，其余的用来记录将要连接的connect_fd
    struct pollfd event_set[INIT_SIZE];
    event_set[0].fd = listen_fd;
    event_set[0].events = POLLRDNORM;

    // 用-1表示这个数组位置还没有被占用
    int i;
    for (i = 1; i < INIT_SIZE; i++) 
    {
        event_set[i].fd = -1;
    }

    for (;;) 
    {
        if ((ready_number = poll(event_set, INIT_SIZE, -1)) < 0) //调用 poll 函数来进行事件检测
        {
            error(1, errno, "poll failed ");
        }

        if (event_set[0].revents & POLLRDNORM) //系统内核检测到监听套接字上的连接建立事件
        {
            socklen_t client_len = sizeof(client_addr);
            connected_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_len);

            //找到一个可以记录该连接套接字的位置
            // 就是把连接描述字 connect_fd 也加入到 event_set 里，
            // 而且说明了我们感兴趣的事件类型为 POLLRDNORM，也就是套接字上有数据可以读
            for (i = 1; i < INIT_SIZE; i++) 
            {
                if (event_set[i].fd < 0)
                {
                    event_set[i].fd = connected_fd;
                    event_set[i].events = POLLRDNORM;
                    break;
                }
            }

            // 如果在数组里找不到这样一个位置，说明我们的 event_set 已经被很多连接充满了，没有办法接收更多的连接了
            if (i == INIT_SIZE) 
            {
                error(1, errno, "can not hold so many clients");
            }
            //一个加速优化能力，因为 poll 返回的一个整数，说明了这次 I/O 事件描述符的个数，
            // 如果处理完监听套接字之后，就已经完成了这次 I/O 复用所要处理的事情，那么我们就可以跳过后面的处理，
            // 再次进入 poll 调用
            if (--ready_number <= 0)
                continue;
        }

        for (i = 1; i < INIT_SIZE; i++) 
        {
            int socket_fd;
            if ((socket_fd = event_set[i].fd) < 0)
                continue;
            //通过检测 revents 的事件类型是 POLLRDNORM 或者 POLLERR，我们可以进行读操作
            if (event_set[i].revents & (POLLRDNORM | POLLERR)) 
            {
                if ((n = read(socket_fd, buf, MAXLINE)) > 0) //读取数据正常之后，再通过 write 操作回显给客户端
                {
                    if (write(socket_fd, buf, n) < 0) 
                    {
                        error(1, errno, "write error");
                    }
                } 
                //如果读到 EOF 或者是连接重置，则关闭这个连接，并且把 event_set 对应的 pollfd 重置
                else if (n == 0 || errno == ECONNRESET) 
                {
                    close(socket_fd);
                    event_set[i].fd = -1;
                } 
                else //读取数据失败
                {
                    error(1, errno, "read error");
                }
                
                //判断如果事件已经被完全处理完之后，直接跳过对 event_set 的循环处理，再次来到 poll 调用
                if (--ready_number <= 0)
                    break;
            }
        }
    }
}
